package info.wangyuan.agent.service.curd.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IORuntimeException;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import info.wangyuan.agent.common.Constant.FileConstant;
import info.wangyuan.agent.common.Enum.FileEventType;
import info.wangyuan.agent.entity.po.FileInfo;
import info.wangyuan.agent.entity.po.KnowledgeBase;
import info.wangyuan.agent.exception.BusinessException;
import info.wangyuan.agent.mapper.FileInfoMapper;
import info.wangyuan.agent.mapper.KnowledgeBaseMapper;
import info.wangyuan.agent.service.curd.FileInfoService;
import info.wangyuan.agent.service.event.RagFileEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;

import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;


/**
 * 知识库文件服务实现类
 *
 * @author Albert
 * @since 2025-07-15 21:47:58
 */
@Slf4j
@Service
public class FileInfoServiceImpl extends ServiceImpl<FileInfoMapper, FileInfo> implements FileInfoService {

    private final KnowledgeBaseMapper knowledgeBaseMapper;
    private final ApplicationEventPublisher publisher;

    public FileInfoServiceImpl(KnowledgeBaseMapper knowledgeBaseMapper, ApplicationEventPublisher publisher) {
        this.knowledgeBaseMapper = knowledgeBaseMapper;
        this.publisher = publisher;
    }

    /**
     * 文件保存
     */
    @Override
    @Transactional
    public void saveFile(List<MultipartFile> files, Integer kbId) {
        // 获取知识库路径
        String kbPath = knowledgeBaseMapper.selectById(kbId).getPath();
        File targetDir = new File(FileConstant.RAG_ROOT_PATH + kbPath);

        // 构建 FileInfo 列表
        List<FileInfo> fileInfoList = files.stream().map(file -> {
            String originalFilename = file.getOriginalFilename();
            if (originalFilename == null) {
                throw new BusinessException("文件名为空");
            }

            // 文件大小校验
            long fileSize = file.getSize();
            if (fileSize > FileConstant.MAX_FILE_SIZE) {
                throw new BusinessException("文件大小不能大于 " + FileConstant.MAX_FILE_SIZE + " MB");
            }

            // 文件保存
            File targetFile = new File(targetDir, originalFilename);
            try {
                file.transferTo(targetFile);
            } catch (IOException e) {
                log.error("文件：{} 保存失败，原因：{}", originalFilename, e.getMessage());
                throw new BusinessException("文件上传失败");
            }

            // 构建 FileInfo
            return FileInfo.builder()
                    .name(originalFilename)
                    .size(fileSize)
                    .suffix(FileUtil.extName(originalFilename))
                    .path(kbPath + File.separator + originalFilename)
                    .knowledgeBaseId(kbId)
                    .build();
        }).toList();

        // 批量保存并获取 ID
        saveBatch(fileInfoList);
        List<Integer> fileInfoIds = fileInfoList.stream()
                .map(FileInfo::getId)
                .toList();

        // 发布事件
        publisher.publishEvent(new RagFileEvent<>(FileEventType.CHUNK, fileInfoIds));
    }


    /**
     * 文件修改
     */
    @Override
    public void canUpdate(FileInfo fileInfo) {
        // 检查同名文件
        boolean exists = count(new LambdaQueryWrapper<FileInfo>()
                .eq(FileInfo::getKnowledgeBaseId, fileInfo.getKnowledgeBaseId())
                .eq(FileInfo::getName, fileInfo.getName())) > 0;

        if (exists) {
            throw new BusinessException("修改失败，知识库中存在同名文件");
        }

        // 获取原文件信息
        FileInfo dbFileInfo = getById(fileInfo);
        if (dbFileInfo == null) {
            throw new BusinessException("文件不存在");
        }

        String originalFilePath = dbFileInfo.getPath();
        String newFileName = fileInfo.getName() + "." + fileInfo.getSuffix();
        String newFilePath = FileUtil.getParent(originalFilePath, 1) + File.separator + newFileName;

        // 重命名文件
        try {
            FileUtil.rename(FileUtil.file(originalFilePath), newFilePath, true, false);
        } catch (Exception e) {
            log.error("文件：{} 重命名失败，原因：{}", fileInfo.getName(), e.getMessage());
            throw new BusinessException("文件重命名失败");
        }

        // 如果知识库变化，则移动文件
        if (!dbFileInfo.getKnowledgeBaseId().equals(fileInfo.getKnowledgeBaseId())) {
            KnowledgeBase targetKb = knowledgeBaseMapper.selectById(fileInfo.getKnowledgeBaseId());
            if (targetKb == null) {
                throw new BusinessException("目标知识库不存在");
            }
            String targetFilePath = targetKb.getPath() + File.separator + newFileName;
            try {
                FileUtil.move(FileUtil.file(newFilePath), FileUtil.file(targetFilePath), true);
                newFilePath = targetFilePath; // 更新路径
            } catch (IORuntimeException e) {
                log.error("文件：{} 移动失败，原因：{}", newFileName, e.getMessage());
                throw new BusinessException("文件移动失败");
            }
        }

        // 更新数据库路径和文件信息
        fileInfo.setPath(newFilePath);
        updateById(fileInfo);

        // 发布事件
        publisher.publishEvent(new RagFileEvent<>(FileEventType.REBUILD_BY_FILE_ID, Collections.singletonList(fileInfo.getId())));
    }


    /**
     * 文件删除
     */
    @Override
    public void canDelete(Integer id) {
        FileInfo fileInfo = getById(id);
        if (fileInfo == null) {
            throw new BusinessException("文件不存在");
        }
        String fileFullName = fileInfo.getName() + "." + fileInfo.getSuffix();
        try {
            FileUtil.del(FileConstant.RAG_ROOT_PATH + fileInfo.getPath());
        } catch (IORuntimeException e) {
            log.error("文件：{} 删除失败，原因：{}", fileFullName, e.getMessage());
            throw new BusinessException("文件删除失败");
        }
        removeById(id);
        publisher.publishEvent(new RagFileEvent<>(FileEventType.REMOVE_BY_FILE_ID, Collections.singletonList(id)));
    }


    /**
     * 文件下载
     */
    @Override
    public ResponseEntity<StreamingResponseBody> downloadFile(Integer id) {
        FileInfo fileInfo = getById(id);
        if (fileInfo == null) {
            throw new BusinessException("文件不存在");
        }
        File file = FileUtil.file(FileConstant.RAG_ROOT_PATH + fileInfo.getPath());

        return ResponseEntity.ok()
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + URLEncoder.encode(file.getName(), StandardCharsets.UTF_8))
                .contentLength(file.length())
                .contentType(MediaType.APPLICATION_OCTET_STREAM)
                .body(outputStream -> FileUtil.writeToStream(file, outputStream));
    }
}
